package de.bk.timecalc.core;

import java.util.Vector;

/**
 * @author Bastian Krol
 * @version $Revision:$ $Date:$ $Author:$
 */
public class LineParser
{
  private static final String UNPARSEABLE_TIME = "Time could not be parsed: ";
  private static final String UNPARSEABLE_TIME_EMPTY_STRING_OR_ONLY_WHITESPACE = "Empty string or only whitespace.";
  private static final String UNPARSEABLE_TIME_TOO_MANY_COLONS = "Too many colons.";
  private static final String UNPARSEABLE_TIME_NO_SECONDS = "No seconds given.";
  private static final String UNPARSEABLE_TIME_NO_MINUTES_AND_NO_SECONDS = "No minutes and no seconds given.";
  private static final String UNPARSEABLE_TIME_NO_MINUTES = "No minutes given.";
  private static final String UNPARSEABLE_TIME_TOO_MANY_DIGITS_BEFORE_COLON = "Too many digits before colon.";
  private static final String UNPARSEABLE_TIME_TOO_MANY_DIGITS_AFTER_COLON = "Too many digits after colon.";
  private static final String UNPARSEABLE_TIME_NOT_ENOUGH_DIGITS = "Not enough digits. (Please use 2, 3 or 4 digits.)";
  private static final String UNPARSEABLE_TIME_TOO_MANY_DIGITS = "Too many digits. (Please use 2, 3 or 4 digits.)";
  private static final String UNPARSEABLE_TIME_NON_NUMERIC_CHARACTER = "Non-numeric character was used: ";
  private static final String UNPARSEABLE_TIME_MINUTE_TOO_BIG = "Minute greater than 59.";
  private static final String UNPARSEABLE_TIME_SECOND_TOO_BIG = "Second greater than 59.";

  public TimeDTO parse(String string) throws LineNotParseableException
  {
    if (string == null)
    {
      throw new IllegalArgumentException("string may not be null");
    }
    string = string.trim();
    if (string.length() == 0)
    {
      throw new LineNotParseableException(UNPARSEABLE_TIME
          + UNPARSEABLE_TIME_EMPTY_STRING_OR_ONLY_WHITESPACE);
    }
    if (string.indexOf(":") >= 0)
    {
      String[] parts = this.split(string, ":");
      if (parts.length > 2)
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_TOO_MANY_COLONS);
      }
      else if (parts.length == 1)
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_NO_SECONDS);
      }
      else if (parts.length == 0)
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_NO_MINUTES_AND_NO_SECONDS);
      }
      String minutesString = parts[0].trim();
      String secondsString = parts[1].trim();
      if (minutesString.equals(""))
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_NO_MINUTES);
      }
      else if (minutesString.length() > 2)
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_TOO_MANY_DIGITS_BEFORE_COLON);
      }
      else if (secondsString.length() > 2)
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_TOO_MANY_DIGITS_AFTER_COLON);
      }
      this.checkNumeric(minutesString);
      this.checkNumeric(secondsString);
      return createTimeDTO(Integer.parseInt(minutesString), Integer
          .parseInt(secondsString));
    }

    this.checkNumeric(string);
    if (string.length() == 4)
    {
      // 4 digits -> mmss [example: 0403 -> 04:03]
      return createTimeDTO(Integer.parseInt(string.substring(0, 2)), Integer
          .parseInt(string.substring(2, 4)));
    }
    else if (string.length() == 3)
    {
      // 3 digits -> mss [example: 403 -> 04:03]
      return createTimeDTO(Integer.parseInt(string.substring(0, 1)), Integer
          .parseInt(string.substring(1, 3)));
    }
    else if (string.length() == 2)
    {
      // 2 digits -> ms [example: 43 -> 04:03]
      return createTimeDTO(Integer.parseInt(string.substring(0, 1)), Integer
          .parseInt(string.substring(1, 2)));
    }
    else if (string.length() == 1)
    {
      throw new LineNotParseableException(UNPARSEABLE_TIME
          + UNPARSEABLE_TIME_NOT_ENOUGH_DIGITS);
    }
    else if (string.length() > 4)
    {
      throw new LineNotParseableException(UNPARSEABLE_TIME
          + UNPARSEABLE_TIME_TOO_MANY_DIGITS);
    }
    return null;
  }

  String[] split(String string, String separator)
  {
    if (separator == null || separator.length() == 0)
    {
      throw new IllegalArgumentException("illegal separator: " + separator);
    }

    Vector parts = new Vector();
    while (string.length() > 0)
    {
      int index = string.indexOf(separator);
      if (index >= 0)
      {
        String part = string.substring(0, index);
        if (part.length() > 0)
        {
          parts.addElement(part);
        }
        string = string.substring(index + separator.length());
      }
      else
      {
        // last part after last separator
        parts.addElement(string.substring(0, string.length()));
        break;
      }
    }
    String[] partArray = new String[parts.size()];
    parts.copyInto(partArray);
    return partArray;
  }

  private void checkNumeric(String string) throws LineNotParseableException
  {
    char[] charArray = string.toCharArray();
    for (int i = 0; i < charArray.length; i++)
    {
      if (!Character.isDigit(charArray[i]))
      {
        throw new LineNotParseableException(UNPARSEABLE_TIME
            + UNPARSEABLE_TIME_NON_NUMERIC_CHARACTER + charArray[i]);
      }
    }
  }

  private TimeDTO createTimeDTO(int minute, int second)
      throws LineNotParseableException
  {
    if (minute > 59)
    {
      throw new LineNotParseableException(UNPARSEABLE_TIME
          + UNPARSEABLE_TIME_MINUTE_TOO_BIG);
    }
    else if (second > 59)
    {
      throw new LineNotParseableException(UNPARSEABLE_TIME
          + UNPARSEABLE_TIME_SECOND_TOO_BIG);
    }
    return new TimeDTO(minute, second);
  }
}
